/*rebuild.cpp
 *This program rebuilds RAID if one of disks fails.
 *Created on: 2013.12.24
 *Author: Petrunin D.S.
 */
#include <iostream>
#include <stdio.h>
#include <cstring>
#include "base.h"


int rebuildRAID01(int nDisks, int nBlocks, int blockS) {
	mapping(nDisks, nBlocks, blockS, true);
	int failed = 0;
	for (int i = 1; i <= nDisks; i++) {
		if (mappedDataArray[i] == NULL)
			failed++;
	}
	if (failed > (nDisks / 2)) {
		cout << "Too many disks are failed. Can't rebuild RAID.\n";
		return 1;
	}

	int* failDs = new int[failed];
	int k = 0;
	for(int i = 1; i <= nDisks; i++) {
		if (mappedDataArray[i] == NULL)	{
			failDs[k] = i; k++;
		}
	}

	for (int j = 1; j <= failed; j++) {
		for(int i = 1; i <= failed; i++) {
			if (failDs[j - 1] + (nDisks / 2) == failDs[i - 1])	{
				cout << "Can't rebuild Disk" << failDs[j - 1] << ". Can't rebuild RAID.\n";
				delete[] failDs;
				return 1;
			}
		}
	}
	FILE* disk;
	char DiskNm[10];
	for(int i = 1; i <= failed; i++) {
		sprintf(DiskNm, "Disk%i", failDs[i - 1]);
		disk = fopen(DiskNm, "wb+");
		if (disk == NULL) {
			cout << "Can't create file Disk" << failDs[i - 1] << ". Can't rebuild RAID.\n";
			delete[] failDs;
			return 1;
		}
		if (failDs[i - 1] <= (nDisks / 2))
			fwrite(mappedDataArray[failDs[i - 1] + (nDisks / 2)], sizeof(char),	blockS * nBlocks, disk);
		else
			fwrite(mappedDataArray[failDs[i - 1] - (nDisks / 2)], sizeof(char), blockS * nBlocks, disk);
		fclose(disk);
	}
	unmapping(nDisks, nBlocks, blockS, true);
	delete[] failDs;
	return 0;
}


int rebuildRAID10(int nDisks, int nBlocks, int blockS) {
	mapping(nDisks, nBlocks, blockS, true);
	int failed = 0;
	for (int i = 1; i <= nDisks; i++) {
		if (mappedDataArray[i] == NULL)
			failed++;
	}
	if (failed > (nDisks / 2)) {
		cout << "Too many disks are failed. Can't rebuild RAID.\n";
		return 1;
	}

	int* failDs = new int[failed];
	int k = 0;
	for (int i = 1; i <= nDisks; i++) {
		if (mappedDataArray[i] == NULL) {
			failDs[k] = i;
			k++;
		}
	}

	for (int j = 1; j <= failed; j++) {
		for (int i = 1; i <= failed; i++) {
			if (((failDs[j - 1] % 2 != 0)&&(failDs[j - 1] + 1 == failDs[i - 1]))
					||((failDs[j - 1] % 2 == 0)&&(failDs[j - 1] - 1 == failDs[i - 1]))){
				cout << "Can't rebuild Disk" << failDs[j - 1] << ". Can't rebuild RAID.\n";
				delete[] failDs;
				return 1;
			}
		}
	}
	FILE* disk;
	char DiskNm[10];
	for (int i = 1; i <= failed; i++) {
		sprintf(DiskNm, "Disk%i", failDs[i - 1]);
		disk = fopen(DiskNm, "wb+");
		if (disk == NULL) {
			cout << "Can't create file Disk" << failDs[i - 1]<< ". Can't rebuild RAID.\n";
			delete[] failDs;
			return 1;
		}
		if (failDs[i - 1] % 2 != 0)
			fwrite(mappedDataArray[failDs[i - 1] + 1],
					sizeof(char), blockS * nBlocks, disk);
		else
			fwrite(mappedDataArray[failDs[i - 1] - 1],
					sizeof(char), blockS * nBlocks, disk);
		fclose(disk);
	}
	unmapping(nDisks, nBlocks, blockS, true);
	delete[] failDs;
	return 0;
}

int main(int argc, char* argv[]) {
	FILE* config = fopen("conf", "r");
	if (config == NULL) {
		cout << "Can't open config file.\n";
		return 1;
	}
	int nDisks, nBlocks, blockS, RAIDLevel;
	fscanf(config, "%i\n%i\n%i\n%i\n", &nDisks, &nBlocks, &blockS, &RAIDLevel);
	fclose(config);
	if (!failedDisk(nDisks, true)) {
		cout << "No need to rebuild RAID.\n";
		return 1;
	}
	switch (RAIDLevel) {
	case 0:
		return rebuildRAID01(nDisks, nBlocks, blockS);
	case 1:
		return rebuildRAID10(nDisks, nBlocks, blockS);
	}
}
